{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 2 - Solution"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Suppose you have a list of all possible eye colors:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "eye_colors = (\"amber\", \"blue\", \"brown\", \"gray\", \"green\", \"hazel\", \"red\", \"violet\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Some other collection (say recovered from a database, or an external API) contains a list of `Person` objects that have an eye color property.\n",
    "\n",
    "Your goal is to create a dictionary that contains the number of people that have the eye color as specified in `eye_colors`. The wrinkle here is that even if no one matches some eye color, say `amber`, your dictionary should still contain an entry `\"amber\": 0`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here is some sample data:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Person:\n",
    "    def __init__(self, eye_color):\n",
    "        self.eye_color = eye_color"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "from random import seed, choices\n",
    "seed(0)\n",
    "persons = [Person(color) for color in choices(eye_colors[2:], k = 50)]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you can see we built up a list of `Person` objects, none of which should have `amber` or `blue` eye colors"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Write a function that returns a dictionary with the correct counts for each eye color listed in `eye_colors`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We're going to use the `Counter` class for this problem.\n",
    "However, simply counting the eye colors in the `person` list is not going to be quite enough:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "from collections import Counter"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "counts = Counter(p.eye_color for p in persons)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Counter({'violet': 12,\n",
       "         'red': 10,\n",
       "         'green': 8,\n",
       "         'gray': 10,\n",
       "         'hazel': 7,\n",
       "         'brown': 3})"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "counts"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you can see we do not have entries for `amber` and `blue` for example."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We could approach this in one of two ways:\n",
    "1. add zero count key/value pairs after the counting has occurred\n",
    "2. or, pre-initialize the `Counter` object with all the possible eye colors set to a count of `0`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's try the first approach:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "counts = Counter(p.eye_color for p in persons)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "result = {color: counts.get(color, 0) for color in eye_colors}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'amber': 0,\n",
       " 'blue': 0,\n",
       " 'brown': 3,\n",
       " 'gray': 10,\n",
       " 'green': 8,\n",
       " 'hazel': 7,\n",
       " 'red': 10,\n",
       " 'violet': 12}"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "result"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And now the second approach, where we initialize our Counter object with zero counts for each eye color first, and **then** do the counting:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "counts = Counter({color: 0 for color in eye_colors})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Counter({'amber': 0,\n",
       "         'blue': 0,\n",
       "         'brown': 0,\n",
       "         'gray': 0,\n",
       "         'green': 0,\n",
       "         'hazel': 0,\n",
       "         'red': 0,\n",
       "         'violet': 0})"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "counts"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you can see we have each color with a count of zero - now we simply update the counter based on the results in the `persons` list:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "counts.update(p.eye_color for p in persons)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Counter({'amber': 0,\n",
       "         'blue': 0,\n",
       "         'brown': 3,\n",
       "         'gray': 10,\n",
       "         'green': 8,\n",
       "         'hazel': 7,\n",
       "         'red': 10,\n",
       "         'violet': 12})"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "counts"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally, let's package up one of those solutions into a function:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "def count_eye_colors(persons, possible_eye_colors):\n",
    "    counts = Counter({color: 0 for color in possible_eye_colors})\n",
    "    counts.update(p.eye_color for p in persons)\n",
    "    return counts"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "which we can then call like this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Counter({'amber': 0,\n",
       "         'blue': 0,\n",
       "         'brown': 3,\n",
       "         'gray': 10,\n",
       "         'green': 8,\n",
       "         'hazel': 7,\n",
       "         'red': 10,\n",
       "         'violet': 12})"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "count_eye_colors(persons, eye_colors)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
